/*
 * #%L
 * java model
 * %%
 * Copyright (C) 2009 - 2011 prefabware.com Stefan Isele
 * %%
 * All Rights Reserved.
 * This program is dual-licensed under the GNU General Public license
 * or a commercial license.
 * You may select to use one or the other of these licenses.
 * To obtain a commercial license please contact Stefan Isele at www.stefan-isele.de
 * Any modifications to this file must keep this entire header intact.
 * Redistribution, except as permitted by the selected license, is prohibited.
 * #L%
 */
package com.prefabware.jmodel;

import java.util.ArrayList;
import java.util.List;

import com.prefabware.commons.QualifiedName;
import com.prefabware.jmodel.annotation.JAnnotatable;
import com.prefabware.jmodel.annotation.JAnnotation;
import com.prefabware.jmodel.annotation.JAnnotationClass;
import com.prefabware.jmodel.annotation.JAnnotations;
import com.prefabware.jmodel.code.JCodable;
import com.prefabware.jmodel.javadoc.JJavadoc;
import com.prefabware.jmodel.reference.JReference;
import com.prefabware.jmodel.reference.JReferenceBase;
import com.prefabware.jmodel.reference.JReferencing;

/**
 * a field, method or constructor of a JType
 * 
 * @author sisele_job
 * 
 */
public abstract class JMember implements JReferencing, JAnnotatable, JCodable,Cloneable {
	protected JJavadoc javadoc;

	public JJavadoc getJavadoc() {
		return javadoc;
	}

	public void setJavadoc(JJavadoc javadoc) {
		this.javadoc = javadoc;
	}

	public String getQualifiedName() {
		String name = null;
		if (this.getDeclaringType() != null) {
			name = this.getDeclaringType().getQualifiedName().getName();
		}
		return new QualifiedName(name, this.getName()).getName();
	}

	public JMember clone() {
		// shallow copy
		JMember clone;
		try {
			clone = (JMember) super.clone();
			//the declaration is mutable, so create a copy of it
			clone.declaration = this.declaration.clone();
		} catch (CloneNotSupportedException e) {
			throw new RuntimeException(e);
		}
		return clone;
	}

	private JAnnotations annotations;
	protected JMemberDeclaration declaration;
	public static final String METHOD_GET_NAME = "getName";

	protected JMember(JMemberDeclaration declaration) {
		super();
		this.declaration = declaration;
		this.annotations = new JAnnotations();
	}

	public void add(JAnnotation element) {
		if (element != null) {
			annotations.add(element);
		}
	}

	public boolean isAnnotationPresent(JAnnotationClass anClass) {
		return annotations.isAnnotationPresent(anClass);
	}

	public JAnnotation getAnnotation(JAnnotationClass annotationClass) {
		return annotations.getAnnotation(annotationClass);
	}

	public JAnnotation getAnnotation(String qualifiedName) {
		return annotations.getAnnotation(qualifiedName);
	}

	public List<JAnnotation> getAnnotations() {
		return annotations.getAnnotations();
	}

	public JMemberDeclaration getDeclaration() {
		return declaration;
	}

	public JType getDeclaringType() {
		return this.getDeclaration().getDeclaringType();
	}

	public String getName() {
		return this.declaration.getName();
	}

	/*
	 * the direct outgoing references that this member has a reference to the
	 * fields type 0 or more references to the type parameters of the fields
	 * type
	 */
	public List<JReference<?>> getReferences() {
		List<JReference<?>> references = new ArrayList<JReference<?>>();
		// add a reference from the type this member belongs to to the fields
		// type
		JType type = this.getDeclaration().getType();
		references.add(JReferenceBase.create(this.getDeclaringType(), type, this));
		// add references to all the type parameters of this member
		// e.g a reference to String if this field is of type List<String>
		references.addAll(this.getDeclaration().getTypeArgumentReferences());
		return references;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((annotations == null) ? 0 : annotations.hashCode());
		result = prime * result + ((declaration == null) ? 0 : declaration.hashCode());
		result = prime * result + ((javadoc == null) ? 0 : javadoc.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		JMember other = (JMember) obj;
		if (annotations == null) {
			if (other.annotations != null)
				return false;
		} else if (!annotations.equals(other.annotations))
			return false;
		if (declaration == null) {
			if (other.declaration != null)
				return false;
		} else if (!declaration.equals(other.declaration))
			return false;
		if (javadoc == null) {
			if (other.javadoc != null)
				return false;
		} else if (!javadoc.equals(other.javadoc))
			return false;
		return true;
	}
}
